//
//  TTProgressHUD.m
//  Tint
//
//  Created by Kevin Wu on 2/28/12.
//  Copyright (c) 2012 Telligenty. All rights reserved.
//

#import "TTProgressHUD.h"
#import "TTCore.h"
#import "TTAdditions.h"
#import "TTBasic.h"
#import "TTRoundProgressView.h"



@interface TTProgressHUD (Private)

/*
 * Update HUD
 */
- (void)updateIndicator;
- (void)updateLabel;
- (void)updateProgress;

/*
 * Operation
 */
- (void)presentUsingAnimation:(NSNumber *)animated;
- (void)dismissUsingAnimation:(NSNumber *)animated;

/*
 * Oritentation
 */
- (void)deviceOrientationDidChange:(NSNotification *)notification;
- (void)setTransformForCurrentOrientation:(BOOL)animated;

@end




@implementation TTProgressHUD


#pragma mark - Accessors

@synthesize delegate = _delegate;


/*
 * Appearance
 */
@synthesize modeType = _modeType;

- (void)setModeType:(TTProgressHUDMode)modeType {
    if (_modeType != modeType) {
        _modeType = modeType;
        [self updateIndicator];
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize opacity = _opacity;

- (void)setOpacity:(CGFloat)opacity {
    if (!TTIsFloatEqualToFloat(opacity, _opacity)) {
        _opacity = opacity;
        [self setNeedsDisplay];
    }
}


@synthesize dimBackground = _dimBackground;

- (void)setDimBackground:(BOOL)dimBackground {
    if (!TTIsBoolEqualToBool(dimBackground, _dimBackground)) {
        _dimBackground = dimBackground;
        [self setNeedsDisplay];
    }
}


@synthesize customIndicator = _customIndicator;

- (void)setCustomIndicator:(UIView *)customIndicator {
    if (customIndicator != _customIndicator) {
        TTRetain(customIndicator, _customIndicator);
        [self updateIndicator];
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize info = _info;

- (void)setInfo:(NSString *)info {
    TTCopy(info, _info);
    [self updateLabel];
    [self setNeedsLayout];
    [self setNeedsDisplay];
}


@synthesize indicator = _indicator;


@synthesize label = _label;




/*
 * Metrics
 */
@synthesize square = _square;

- (void)setSquare:(BOOL)square {
    if (!TTIsBoolEqualToBool(square, _square)) {
        _square = square;
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize minSize = _minSize;

- (void)setMinSize:(CGSize)minSize {
    if (!TTIsSizeEqualToSize(minSize, _minSize)) {
        _minSize = TTSizeMake(minSize.width, minSize.height);
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize margin = _margin;

- (void)setMargin:(CGFloat)margin {
    if (!TTIsFloatEqualToFloat(margin, _margin)) {
        _margin = margin;
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize spacing = _spacing;

- (void)setSpacing:(CGFloat)spacing {
    if (!TTIsFloatEqualToFloat(spacing, _spacing)) {
        _spacing = spacing;
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize offset = _offset;

- (void)setOffset:(CGPoint)offset {
    if (!TTIsPointEqualToPoint(offset, _offset)) {
        _offset = TTPointMake(offset.x, offset.y);
        [self setNeedsLayout];
        [self setNeedsDisplay];
    }
}


@synthesize hudWidth = _hudWidth;


@synthesize hudHeight = _hudHeight;



/*
 * Progress
 */
@synthesize progress = _progress;

- (void)setProgress:(CGFloat)progress {
    if (progress>=0 && progress<=1.0) {
        _progress = progress;
        [self updateProgress];
    }
}



/*
 * Time
 */
@synthesize graceTime = _graceTime;

@synthesize minShowTime = _minShowTime;

@synthesize startDate = _startDate;



#pragma mark - Memory

- (id)init {
    self = [self initWithFrame:TTRectWithW(120.0)];
    if (self) {
        [self updateIndicator];
        [self updateLabel];
        [self updateProgress];
        
        [[NSNotificationCenter defaultCenter] addObserver:self 
                                                 selector:@selector(deviceOrientationDidChange:) 
                                                     name:UIDeviceOrientationDidChangeNotification 
                                                   object:nil];
    }
    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    _delegate = nil;
    
    TTRelease(_customIndicator);
    TTRelease(_info);
    TTRelease(_indicator);
    TTRelease(_label);
    
    TTRelease(_startDate);
    [super dealloc];
}



#pragma mark - UIView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        
        _delegate = nil;
        
        // Appearance
        _modeType = TTProgressHUDModeIndeterminate;
        _opacity = 0.8;
        _dimBackground = NO;
        _customIndicator = nil;
        _info = nil;
        _indicator = nil;
        _label = nil;
        
        // Metrics
        _square = YES;
        _minSize = TTSizeSquare(120.0);
        _margin = 20.0;
        _spacing = 8.0;
        _offset = TTPointZero();
        _hudWidth = 0.0;
        _hudHeight = 0.0;
        
        // Progress
        _progress = 0.0;
        
        // Time
        _graceTime = 0.0;
        _minShowTime = 1.0;
        _startDate = nil;
        
        
        self.opaque = NO;
        self.backgroundColor = [UIColor clearColor];
        self.alpha = 0.0f;
        self.autoresizingMask = TTViewAutoresizingKeepSize;
    }
    return self;
}

- (void)layoutSubviews {
    // Position the indicator
    _indicator.frame = TTRectMake((self.bounds.size.width - _indicator.width)/2.0 + _offset.x, 
                                  (self.bounds.size.height - _indicator.height)/2.0 + _offset.y, 
                                  _indicator.width, 
                                  _indicator.height);
	
    // Compute HUD dimensions based on indicator size (add margin to HUD border)
    _hudWidth = _indicator.width + 2*_margin;
    _hudHeight = _indicator.height + 2*_margin;
    
    
    if (_label) {
        // Get size of label text
        CGSize dims = [_info sizeWithFont:_label.font];
		
        // Compute label dimensions based on font metrics if size is larger than max then clip the label width
        float lHeight = dims.height;
        float lWidth = dims.width;
        if (dims.width > (self.bounds.size.width - 4*_margin))
            lWidth = self.bounds.size.width - 4*_margin;
		
        // Update HUD size
        if (_hudWidth < (lWidth + 2*_margin)) _hudWidth = lWidth + 2*_margin;
        _hudHeight = _hudHeight + lHeight + _spacing;
		
        // Move indicator to make room for the label
        _indicator.topY = _indicator.topY - (lHeight + _spacing)/2.0;
		
        // Set the label position and dimensions
        _label.frame = TTRectMake((self.bounds.size.width - lWidth)/2.0 + _offset.x, 
                                  _indicator.bottomY + _spacing, 
                                  lWidth, 
                                  lHeight);
    }
	
	if (_square) {
		CGFloat max = MAX(_hudWidth, _hudHeight);
		if (max <= (self.bounds.size.width - 2*_margin)) _hudWidth = max;
        if (max <= (self.bounds.size.height - 2*_margin)) _hudHeight = max;
	}
	
	if (_hudWidth < _minSize.width) _hudWidth = _minSize.width;
	if (_hudHeight < _minSize.height) _hudHeight = _minSize.height;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    if (_dimBackground) {
        //Gradient colours
        size_t gradLocationsNum = 2;
        CGFloat gradLocations[2] = {0.0f, 1.0f};
        CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; 
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
		CGColorSpaceRelease(colorSpace);
        
        //Gradient center
        CGPoint gradCenter = CGPointMake(self.bounds.size.width/2.0, self.bounds.size.height/2.0);
        
        //Gradient radius
        float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height);
        
        //Gradient draw
        CGContextDrawRadialGradient (context, gradient, gradCenter, 
                                     0, gradCenter, gradRadius, 
                                     kCGGradientDrawsAfterEndLocation);
		CGGradientRelease(gradient);
    }
    
    // Center HUD
    CGRect allRect = self.bounds;
    
    // Draw rounded HUD bacgroud rect
    CGRect boxRect = CGRectMake(roundf((allRect.size.width - _hudWidth) / 2) + _offset.x,
                                roundf((allRect.size.height - _hudHeight) / 2) + _offset.y, 
                                _hudWidth, _hudHeight);
    
	// Corner radius
	float radius = 10.0f;
	
    CGContextBeginPath(context);
    CGContextSetGrayFillColor(context, 0.0f, _opacity);
    CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
    CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
    CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
    CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
    CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
    CGContextClosePath(context);
    CGContextFillPath(context);
}




#pragma mark - Public


- (void)presentAnimated:(BOOL)animated {
    if (_graceTime > 0.0) {
        [self performSelector:@selector(presentUsingAnimation:) 
                   withObject:[NSNumber numberWithBool:animated] 
                   afterDelay:_graceTime];
    } else {
        [self presentUsingAnimation:[NSNumber numberWithBool:animated]];
    }
}

- (void)dismissAnimated:(BOOL)animated {
    if ((_minShowTime > 0.0) && _startDate) {
        NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:_startDate];
        if (timeInterval < _minShowTime) {
            [self performSelector:@selector(dismissUsingAnimation:) 
                       withObject:[NSNumber numberWithBool:animated] 
                       afterDelay:(_minShowTime - timeInterval)];
            return;
        }
    }
    
    [self dismissUsingAnimation:[NSNumber numberWithBool:animated]];
}

- (void)showAnimated:(NSNumber *)animated {
    [self presentAnimated:[animated boolValue]];
}

- (void)hideAnimated:(NSNumber *)animated {
    [self dismissAnimated:[animated boolValue]];
}

- (void)dismissAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
    [self performSelector:@selector(dismissUsingAnimation:) 
               withObject:[NSNumber numberWithBool:animated] 
               afterDelay:delay];
}


+ (TTProgressHUD *)makeHUDForView:(UIView *)view {
    TTProgressHUD *hud = [self findHUDInView:view];
    if (hud) {
        // If the hud is about to present or dismiss, cancel it.
        [self cancelPreviousPerformRequestsWithTarget:hud];
    } else {
        hud = [TTProgressHUD make];
        [view addSubview:hud];
    }
    
    hud.frame = view.bounds;
    if (TTIsInstance(view, [UIWindow class])) [hud setTransformForCurrentOrientation:NO];
    return hud;
}

+ (TTProgressHUD *)presentHUDInView:(UIView *)view info:(NSString *)info animated:(BOOL)animated {
    
    TTProgressHUD *hud = [self findHUDInView:view];
    if (hud) {
        // If the hud is about to present or dismiss, cancel it.
        [self cancelPreviousPerformRequestsWithTarget:hud];
    } else {
        hud = [TTProgressHUD make];
        [view addSubview:hud];
    }
    
    hud.frame = view.bounds;
    if (TTIsInstance(view, [UIWindow class])) [hud setTransformForCurrentOrientation:NO];
    hud.info = info;
    [hud presentAnimated:animated];
    
    return hud;
}

+ (void)dismissHUDFromView:(UIView *)view animated:(BOOL)animated {
    TTProgressHUD *hud = [self findHUDInView:view];
    if (hud) {
        // If the hud is about to present or dismiss, cancel it.
        [self cancelPreviousPerformRequestsWithTarget:hud];
        [hud dismissAnimated:animated];
    }
}

+ (void)dismissHUDFromView:(UIView *)view state:(BOOL)state info:(NSString *)info animated:(BOOL)animated {
    TTProgressHUD *hud = [self findHUDInView:view];
    if (hud) {
        // If the hud is about to present or dismiss, cancel it.
        [self cancelPreviousPerformRequestsWithTarget:hud];
        
        if (state) {
            hud.modeType = TTProgressHUDModeSuccess;
        } else {
            hud.modeType = TTProgressHUDModeFailure;
        }
        
        hud.info = info;
        [hud dismissAnimated:animated afterDelay:1.0];
    }
}




+ (TTProgressHUD *)findHUDInView:(UIView *)view {
    NSArray *huds = [view subviewsWithClass:[self class]];
    return [huds objectOrNilAtIndex:0];
}

+ (BOOL)containsHUDInView:(UIView *)view {
    return ([self findHUDInView:view] != nil);
}


@end




@implementation TTProgressHUD (Private)


#pragma mark - Update HUD

- (void)updateIndicator {
    if (_indicator) {
        [_indicator removeFromSuperview];
        TTRelease(_indicator);
    }
	
    if (_modeType == TTProgressHUDModeDeterminate) {
        _indicator = [[TTRoundProgressView alloc] init];
    } else if (_modeType == TTProgressHUDModeCustomView && self.customIndicator != nil) {
        _indicator = [_customIndicator retain];
    } else if (_modeType == TTProgressHUDModeSuccess) {
        _indicator = [[UIImageView alloc] initWithImage:TTTintBundleImage(@"Images/ProgressHUD/success.png")];
    } else if (_modeType == TTProgressHUDModeFailure) {
        _indicator = [[UIImageView alloc] initWithImage:TTTintBundleImage(@"Images/ProgressHUD/failure.png")];
    } else {
        UIActivityIndicatorView *aiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
        [aiv startAnimating];
        _indicator = aiv;
    }
	
    [self addSubview:_indicator];
}

- (void)updateLabel {
    if (TTIsStringWithText(_info)) {
        if (_label == nil) {
            _label = [[UILabel alloc] init];
            _label.adjustsFontSizeToFitWidth = NO;
            _label.textAlignment = UITextAlignmentCenter;
            _label.opaque = NO;
            _label.backgroundColor = [UIColor clearColor];
            _label.textColor = [UIColor whiteColor];
            _label.font = [UIFont boldSystemFontOfSize:16.0];
            [self addSubview:_label];
        }
        
        _label.text = _info;
        
    } else {
        [_label removeFromSuperview];
        TTRelease(_label);
    }
}

- (void)updateProgress {
    if (_indicator && [_indicator respondsToSelector:@selector(setProgress:)]) {
        [(UIProgressView *)_indicator setProgress:_progress];
    }
}



#pragma mark - Operation

- (void)presentUsingAnimation:(NSNumber *)animated {
    
    [(NSObject *)_delegate runSelectorOnMainThread:@selector(hudWillPresent:) withObject:self];
    
    
    NSDate *now = [NSDate date];
    TTRetain(now, _startDate);
    
    // Fade in
    if ([animated boolValue]) {
        [UIView animateWithDuration:0.3 
                         animations:^{
                             self.alpha = 1.0f;
                         }];
    } else {
        self.alpha = 1.0f;
    }
    
}

- (void)dismissUsingAnimation:(NSNumber *)animated {
    if ([animated boolValue]) {
        [UIView animateWithDuration:0.3 
                         animations:^{
                             self.alpha = 0.0f;
                         } completion:^(BOOL finished) {
                             [(NSObject *)_delegate runSelectorOnMainThread:@selector(hudDidDismiss:) withObject:self];
                             TTRelease(_startDate);
                             [self removeFromSuperview];
                         }];
    } else {
        self.alpha = 0.0f;
        
        [(NSObject *)_delegate runSelectorOnMainThread:@selector(hudDidDismiss:) withObject:self];
        TTRelease(_startDate);
        [self removeFromSuperview];
    }
}



#pragma mark - Oritentation

- (void)deviceOrientationDidChange:(NSNotification *)notification {
    if (self.superview == nil)
		return;
    
	if (TTIsInstance(self.superview, [UIWindow class])) {
		[self setTransformForCurrentOrientation:YES];
	} else {
        self.frame = self.superview.bounds;
		[self setNeedsDisplay];
	}
}

- (void)setTransformForCurrentOrientation:(BOOL)animated {
    if (self.superview == nil)
        return;
    
    
    if (TTIsInterfaceLandscape()) {
        self.bounds = TTRectWithWH(self.superview.bounds.size.height, self.superview.bounds.size.width);
    } else {
        self.bounds = TTRectWithWH(self.superview.bounds.size.width, self.superview.bounds.size.height);
    }
    [self setNeedsDisplay];
    
    
    CGAffineTransform rotationTransform = TTSupportedRotateTransformForOrientation(TTInterfaceOrientation());
    
    if (animated) {
        [UIView animateWithDuration:0.3 animations:^{
            [self setTransform:rotationTransform];
        }];
    } else {
        [self setTransform:rotationTransform];
    }
}

@end
